/*
 * Unidata Platform
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 *
 * Commercial License
 * This version of Unidata Platform is licensed commercially and is the appropriate option for the vast majority of use cases.
 *
 * Please see the Unidata Licensing page at: https://unidata-platform.com/license/
 * For clarification or additional options, please contact: info@unidata-platform.com
 * -------
 * Disclaimer:
 * -------
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 */
package org.unidata.mdm.rest.meta.converter;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.unidata.mdm.meta.context.UpsertDataModelContext;
import org.unidata.mdm.meta.context.UpsertDataModelContext.UpsertDataModelContextBuilder;
import org.unidata.mdm.meta.type.model.SimpleDataType;
import org.unidata.mdm.meta.type.model.attributes.AttributeGroup;
import org.unidata.mdm.meta.type.model.attributes.CodeMetaModelAttribute;
import org.unidata.mdm.meta.type.model.entities.LookupEntity;
import org.unidata.mdm.rest.meta.ro.AbstractEntityRO;
import org.unidata.mdm.rest.meta.ro.CodeAttributeRO;
import org.unidata.mdm.rest.meta.ro.GroupsRO;
import org.unidata.mdm.rest.meta.ro.LookupEntityRO;

/**
 * @author Mikhail Mikhailov
 *
 */
public class LookupEntityDefinitionToLookupEntityDefConverter {

    /**
     * Instantiation disabled.
     */
    private LookupEntityDefinitionToLookupEntityDefConverter() {
        super();
    }

    /**
     * Converts a REST lookup entity definition DTO into internal format.
     * @param source REST DTO
     * @param draftId
     * @return internal
     */
    public static UpsertDataModelContextBuilder convert(LookupEntityRO source, long draftId) {

        LookupEntity target = new LookupEntity();

        target.setDashboardVisible(source.isDashboardVisible());
        target.setValidityPeriod(PeriodBoundaryConverter.from(source.getValidityPeriod()));
        target.setMergeSettings(MergeSettingsConverter.from(source.getMergeSettings()));
        target.setExternalIdGenerationStrategy(ExternalIdGenerationStrategyConverter.from(source.getExternalIdGenerationStrategy()));
        target.setGroupName(source.getGroupName());


        copyAbstractEntityData(source, target);
        copyCodeAttribute(source.getCodeAttribute(), target.withCodeAttribute(new CodeMetaModelAttribute()).getCodeAttribute());

        for (CodeAttributeRO codeAttributeDefinition : source.getAliasCodeAttributes()) {
            CodeMetaModelAttribute codeAttributeDef = new CodeMetaModelAttribute();
            copyCodeAttribute(codeAttributeDefinition, codeAttributeDef);
            target.getAliasCodeAttributes().add(codeAttributeDef);
        }

        if (source.getAttributeGroups() != null) {
            convertAttributeGroups(source.getAttributeGroups(), target.getAttributeGroups());
        }

        return UpsertDataModelContext.builder()
                .lookupEntitiesUpdate(Collections.singletonList(target))
                .draftId(draftId > 0 ? draftId : null);
    }

    /**
     * Convert Request object to Model object.
     * @param sourceAttributeGroups - will be used for filling
     * @param targetAttributeGroups - will be filled
     */
    private static void convertAttributeGroups(List<GroupsRO> sourceAttributeGroups, List<AttributeGroup> targetAttributeGroups) {
        for (GroupsRO attributeGroup : sourceAttributeGroups) {
            AttributeGroup attributeGroupDef = new AttributeGroup()
                    .withColumn(attributeGroup.getColumn())
                    .withRow(attributeGroup.getRow())
                    .withTitle(attributeGroup.getTitle())
                    .withAttributes(attributeGroup.getAttributes());
            targetAttributeGroups.add(attributeGroupDef);
        }
    }

    /**
     * Copy code attribute.
     * @param source the source
     * @param target target
     */
    private static void copyCodeAttribute(CodeAttributeRO source, CodeMetaModelAttribute target) {
        SimpleAttributeDefConverter.copyAbstractAttributeData(source, target);

        // CA type must always be set
        Objects.requireNonNull(source.getSimpleDataType(), "Code attribute TYPE must not be null!");

        target.setNullable(source.isNullable());
        target.setSimpleDataType(SimpleDataType.valueOf(source.getSimpleDataType().name()));
        target.setUnique(source.isUnique());
        target.setSearchable(source.isSearchable());
        target.setDisplayable(source.isDisplayable());
        target.setMainDisplayable(source.isMainDisplayable());
        target.setValueGenerationStrategy(ExternalIdGenerationStrategyConverter.from(source.getExternalIdGenerationStrategy()));
    }

    /**
     * Copy abstract entity definition from REST to internal.
     * @param source REST source
     * @param target internal
     */
    private static void copyAbstractEntityData(AbstractEntityRO source, LookupEntity target) {
        target.setName(source.getName());
        target.setDisplayName(source.getDisplayName());
        target.setDescription(source.getDescription());

        SimpleAttributeDefConverter.copySimpleAttributeDataList(source.getSimpleAttributes(), target.getSimpleAttribute());
        ArrayAttributeDefConverter.copySimpleAttributeDataList(source.getArrayAttributes(), target.getArrayAttribute());

        target.withCustomProperties(CustomPropertiesConverter.from(source.getCustomProperties()));
    }
}
